/**
 * App中所有组件state的全局对象 stateTree 
 *  1. 保存在一个全局对象中，不安全 ——> 需要安全访问 ACL 
 *  2. 增加修改 state 的门槛，不方便维护 
 * 
 * 解决办法：通过一个中间层来管理和维护组件的state对象 ——> 代理对象(IOC容器) Store
 *   组件与代理对象(IOC容器) Store 之间，通过 `发布/订阅` 模式进行通信
 *   解耦了View(组件渲染)与 Model(组件的数据state对象更新)：数据处理setState()、render组件渲染(挂载)到View页面
 */
function renderTitle(title) {
    let element = document.querySelector('#title');
    element.innerHTML = title.text;
    element.style.color = title.color;
}
function renderContent(content) {
    let element = document.querySelector('#content');
    element.innerHTML = content.text;
    element.style.color = content.color;
}

const UPDATE_TITLE_COLOR = 'UPDATE_TITLE_COLOR';
const UPDATE_TITLE_TEXT = 'UPDATE_TITLE_TEXT';
const UPDATE_CONTENT_COLOR = 'UPDATE_CONTENT_COLOR';
const UPDATE_CONTENT_TEXT = 'UPDATE_CONTENT_TEXT';

//创建一个仓库Store，Store其实是一个代理对象(容器)，只不过可以引用闭包变量
function createStore(reducer, preloadedState) {
    let state = preloadedState;
    let listeners = [];
    
    function getState() {
        // return state;
        return JSON.parse(JSON.stringify(state));
    }
    //派发分发 action 动作/指令
    // action动作是一个普通的JS对象，只有一个属性是必须的。type, 其它属性随意 
    function dispatch(action) {
        //接收新的动作后，通过当前状态和新动作计算出新状态
        state = reducer(state, action);
        //然后通过所有的监听函数执行
        listeners.forEach(listener => listener());
    }
    //派发了一个动作获取初始值，其实在redux内部是派发一个INIT: '@@redux/INIT'动作
    dispatch({ type: '@@redux/INIT' });
    //订阅，供外界订阅本仓库中状态的变化 ，如果状态 变化 了会执行订阅的逻辑 
    function subscribe(listener) {
        listeners.push(listener);
        //返回一个取消订阅函数
        return function () {
            listeners = listeners.filter(item => item != listener)
        }
    }
    return {
        getState, dispatch, subscribe
    }
}

let initState = {
    title: { color: 'red', text: '标题' },
    content: { color: 'green', text: '内容' }
}
//reduce 处理器。根据老的状态和拿到的动作，返回新的状态 
let reducer = function (state = initState, action) {
    switch (action.type) {
        case UPDATE_TITLE_COLOR:// { type: UPDATE_TITLE_COLOR, color: 'purple' }
            return { ...state, title: { ...state.title, color: action.color } };
        case UPDATE_TITLE_TEXT:// { type: UPDATE_TITLE_TEXT, text: '新标题' }
            return { ...state, title: { ...state.title, text: action.text } };
        case UPDATE_CONTENT_COLOR:// { type: UPDATE_CONTENT_COLOR, color: 'purple' }
            return { ...state, content: { ...state.content, color: action.color } };
        case UPDATE_CONTENT_TEXT:// { type: UPDATE_CONTENT_TEXT, text: '新内容' }
            return { ...state, content: { ...state.content, text: action.text } };
        default:
            return state;
    }
} 

let store = createStore(reducer, {
    title: { color: 'red', text: '初始标题' },
    content: { color: 'green', text: '初始内容' }
});
function render() {
    renderTitle(store.getState().title);
    renderContent(store.getState().content);
}
render();
let unsubscribe = store.subscribe(render);
//为了方便action的书写和避免写错，一般我们会写一个函数来生成对应的action
function updateTitleColor(color) {
    return { type: UPDATE_TITLE_COLOR, color };
}

setTimeout(() => {
    store.dispatch();
    unsubscribe(updateTitleColor('orange'));
    store.dispatch({ type: UPDATE_CONTENT_TEXT, text: '新内容' });
}, 1000);

/**
 * appState App中所有组件的state对象，这样保存在一个全局对象中 stateTree 
 *  1. 保存在一个全局对象中，不安全 ——> 需要安全访问 ACL：Store容器维护
 *  2. 增加修改 state对象的门槛，不方便维护
 *  3. 成功了保护了我们的状态，不能直接外部修改
 *  4. 需要持久化：配置中心
 * 
 * 带着这些问题，React引入了redux 
 */